﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using iTextSharp.text;
using iTextSharp.text.html.simpleparser;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;

namespace Docsbox
{
    public class PdfHelper
    {
        private ICredentials _credential;

        public PdfHelper()
        {

        }

        /// <summary>
        /// Pass NetworkCredential if the source file is located in web and require authorization
        /// </summary>
        /// <param name="credential"></param>
        public PdfHelper(ICredentials credential)
        {
            _credential = credential;
        }

        public ICredentials Credential
        {
            set { _credential = value; }
            get { return _credential; }
        }

        private string _htmlContent = string.Empty;
        public void LoadHtml(string htmlPath)
        {
            _htmlContent = File.ReadAllText(htmlPath);
        }

        public byte[] CreatePdf()
        {
            if(!string.IsNullOrEmpty(_htmlContent))
            {
                return CreatePdf(_htmlContent);
            }
            return null;
        }

        //create PDF byte[] output by passing html content text
        public byte[] CreatePdf(string htmlContent)
        {
            var document = new Document(PageSize.A4, 50, 50, 25, 25);

            var output = new MemoryStream();
            var writer = PdfWriter.GetInstance(document, output);

            document.Open();

            var parsedHtmlElements = HTMLWorker.ParseToList(new StringReader(htmlContent), null);
            foreach (var htmlElement in parsedHtmlElements)
            {
                document.Add(htmlElement as IElement);
            }

            document.Close();

            return output.GetBuffer();
        }

        //create PDF by passing htmlContent and save PDF to destination Location
        public void CreatePdf(string htmlContent, string destination)
        {
            byte[] result = CreatePdf(htmlContent);
            if (IsUrl(destination))
            {

                var client = new WebClient();
                client.Credentials = _credential;
                client.UploadData(destination, result);
            }
            else
            {
                File.WriteAllBytes(destination, result);
            }
            File.WriteAllBytes(destination, result);
        }

        //create PDF by passing htmlContent and save PDF to destination Location,return the PDF byte data back
        public byte[] SavePdf(string htmlContent, string destination)
        {
            byte[] result = CreatePdf(htmlContent);
            if (IsUrl(destination))
            {

                var client = new WebClient();
                client.Credentials = _credential;
                client.UploadData(destination, result);
            }
            else
            {
                File.WriteAllBytes(destination, result);
            }
            File.WriteAllBytes(destination, result);
            return result;
        }
        /// <summary>
        /// create a PDF by passing image byte[] data
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        public byte[] CreatePdf(byte[] image)
        {
            using (var ms = new MemoryStream())
            {
                var document = new Document(PageSize.A5, 20, 20, 40, 40);
                PdfWriter.GetInstance(document, ms).SetFullCompression();
                document.Open();
                Image img = Image.GetInstance(image);
                img.ScaleToFit(document.PageSize.Width - 40, document.PageSize.Height - 80);
                document.Add(img);
                document.Close();
                return ms.ToArray();
            }
        }

        /// <summary>
        /// Search the PDF document for the keyword and return the first instance of the keyword existance page number
        /// </summary>
        /// <param name="file"></param>
        /// <param name="keyword"></param>
        /// <returns></returns>
        public int SearchPdf(string file, string keyword)
        {

            if (File.Exists(file))
            {
                var reader = new PdfReader(file);

                for (int page = 1; page <= reader.NumberOfPages; page++)
                {
                    ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
                    string current = PdfTextExtractor.GetTextFromPage(reader, page, strategy);

                    current =
                        Encoding.UTF8.GetString(ASCIIEncoding.Convert(Encoding.Default, Encoding.UTF8,
                                                                      Encoding.Default.GetBytes(current)));
                    if (current.Contains(keyword))
                        return page;
                    reader.Close();
                }
            }
            return 0;
        }
        /// <summary>
        /// read PDF 
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public string ReadPdf(string filename)
        {

            var reader = new PdfReader(filename);
            return ReadPdf(reader);

        }
        /// <summary>
        /// read pdf document and return the text of the document
        /// </summary>
        /// <param name="contents"></param>
        /// <returns></returns>
        public string ReadPdf(byte[] contents)
        {

            var reader = new PdfReader(contents);
            return ReadPdf(reader);
        }

        public string ReadPdf(Stream stream)
        {
            var reader = new PdfReader(stream);
            return ReadPdf(reader);
        }

        private string ReadPdf(PdfReader reader)
        {
            var text = new StringBuilder();

            for (int page = 1; page <= reader.NumberOfPages; page++)
            {
                ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
                string current = PdfTextExtractor.GetTextFromPage(reader, page, strategy);

                current =
                    Encoding.UTF8.GetString(ASCIIEncoding.Convert(Encoding.Default, Encoding.UTF8,
                                                                    Encoding.Default.GetBytes(current)));
                text.Append(current);
                reader.Close();
            }
            return text.ToString();
        }


        public string ReadPdf(string file, int pageNumber)
        {
            return ReadPdf(file, pageNumber, pageNumber);
        }
        public string ReadPdf(Stream stream, int pageNumber)
        {
            return ReadPdf(stream, pageNumber, pageNumber);
        }
        public string Read(byte[] contents, int pageNumber)
        {
            return ReadPdf(contents, pageNumber, pageNumber);
        }

        public string ReadPdf(string path, int firstPage, int lastPage)
        {
            if (File.Exists(path))
            {
                var reader = new PdfReader(path);
                return Read(reader, firstPage, lastPage);
            }
            return string.Empty;
        }

        public string ReadPdf(Stream stream, int firstPage, int lastPage)
        {
            var reader = new PdfReader(stream);
            return Read(reader, firstPage, lastPage);
        }

        public string ReadPdf(byte[] content, int firstPage, int lastPage)
        {
            var reader = new PdfReader(content);
            return Read(reader, firstPage, lastPage);

        }

        private string Read(PdfReader reader, int firstPage, int lastPage)
        {
            var text = new StringBuilder();

            for (int page = 1; page <= reader.NumberOfPages; page++)
            {
                if (page >= firstPage && page <= lastPage)
                {
                    ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
                    string current = PdfTextExtractor.GetTextFromPage(reader, page, strategy);

                    current =
                        Encoding.UTF8.GetString(ASCIIEncoding.Convert(Encoding.Default, Encoding.UTF8,
                                                                        Encoding.Default.GetBytes(current)));
                    text.Append(current);
                    reader.Close();
                }
            }
            return text.ToString();
        }

        public int GetPdfNumberOfPages(byte[] contents)
        {
            var reader = new PdfReader(contents);
            return reader.NumberOfPages;
        }

        public int GetPdfNumberOfPages(Stream stream)
        {
            var reader = new PdfReader(stream);
            return reader.NumberOfPages;
        }

        public int GetPdfNumberOfPages(string path)
        {

            if (File.Exists(path))
            {
                var reader = new PdfReader(path);
                return reader.NumberOfPages;
            }
            return 0;
        }

        public byte[] MergePdf(string[] sourceFilesPath)
        {

            Document document = new Document();
            MemoryStream output = new MemoryStream();

            try
            {
                // Initialize pdf writer
                PdfWriter writer = PdfWriter.GetInstance(document, output);
                writer.PageEvent = new PdfPageEvents();

                // Open document to write
                document.Open();
                PdfContentByte content = writer.DirectContent;

                // Iterate through all pdf documents
                for (int fileCounter = 0; fileCounter < sourceFilesPath.Length; fileCounter++)
                {
                    // Create pdf reader
                    string filePath = sourceFilesPath[fileCounter];
                    PdfReader reader;
                    if (IsUrl(filePath))
                    {
                        var client = new WebClient();
                        client.Credentials = _credential;
                        byte[] data = client.DownloadData(filePath);
                        reader = new PdfReader(data);
                    }
                    else
                    {
                        reader = new PdfReader(filePath);
                    }
                    int numberOfPages = reader.NumberOfPages;

                    // Iterate through all pages
                    for (int currentPageIndex = 1; currentPageIndex <=
                                       numberOfPages; currentPageIndex++)
                    {
                        // Determine page size for the current page
                        document.SetPageSize(
                           reader.GetPageSizeWithRotation(currentPageIndex));

                        // Create page
                        document.NewPage();
                        PdfImportedPage importedPage =
                          writer.GetImportedPage(reader, currentPageIndex);


                        // Determine page orientation
                        int pageOrientation = reader.GetPageRotation(currentPageIndex);
                        if ((pageOrientation == 90) || (pageOrientation == 270))
                        {
                            if (pageOrientation == 90)
                            {
                                content.AddTemplate(importedPage, 0, -1f, 1f, 0, 0,
                                   reader.GetPageSizeWithRotation(currentPageIndex).Height);
                            }
                            if (pageOrientation == 270)
                            {
                                content.AddTemplate(importedPage, 0, 1f, -1f, 0,
                                   reader.GetPageSizeWithRotation(currentPageIndex).Width, 0);
                            }
                        }
                        else
                        {
                            content.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                throw new Exception("There has an unexpected exception" +
                      " occured during the pdf merging process.", exception);
            }
            finally
            {
                document.Close();
            }
            return output.GetBuffer();
        }

        public void MergePdf(string destinationFile, string[] sourceFiles)
        {
            try
            {
                int index = 0;
                string outFile = destinationFile;
                Document document = null;
                PdfCopy writer = null;
                while (index < sourceFiles.Length)
                {
                    string filePath = sourceFiles[index];
                    PdfReader reader;
                    if (IsUrl(filePath))
                    {
                        var client = new WebClient();
                        client.Credentials = _credential;
                        byte[] data = client.DownloadData(filePath);
                        reader = new PdfReader(data);
                    }
                    else
                    {
                        reader = new PdfReader(filePath);
                    }

                    int n = reader.NumberOfPages;

                    if (index == 0)
                    {
                        document = new Document(reader.GetPageSizeWithRotation(1));
                        writer = new PdfCopy(document, new FileStream(outFile, FileMode.Create));

                        document.Open();
                    }

                    PdfImportedPage page;

                    for (int i = 0; i < n; )
                    {
                        ++i;
                        page = writer.GetImportedPage(reader, i);
                        writer.AddPage(page);
                    }

                    PRAcroForm form = reader.AcroForm;
                    if (form != null)
                    {
                        writer.CopyAcroForm(reader);
                    }
                    index++;
                }
                document.Close();
            }
            catch (Exception)
            {

                throw;
            }
        }

        public byte[] MergePdf(List<byte[]> sourceFiles)
        {
            Document document = new Document();
            MemoryStream output = new MemoryStream();

            try
            {
                // Initialize pdf writer
                PdfWriter writer = PdfWriter.GetInstance(document, output);
                writer.PageEvent = new PdfPageEvents();

                // Open document to write
                document.Open();
                PdfContentByte content = writer.DirectContent;

                // Iterate through all pdf documents
                for (int fileCounter = 0; fileCounter < sourceFiles.Count; fileCounter++)
                {
                    // Create pdf reader
                    PdfReader reader = new PdfReader(sourceFiles[fileCounter]);
                    int numberOfPages = reader.NumberOfPages;

                    // Iterate through all pages
                    for (int currentPageIndex = 1; currentPageIndex <=
                                       numberOfPages; currentPageIndex++)
                    {
                        // Determine page size for the current page
                        document.SetPageSize(
                           reader.GetPageSizeWithRotation(currentPageIndex));

                        // Create page
                        document.NewPage();
                        PdfImportedPage importedPage =
                          writer.GetImportedPage(reader, currentPageIndex);


                        // Determine page orientation
                        int pageOrientation = reader.GetPageRotation(currentPageIndex);
                        if ((pageOrientation == 90) || (pageOrientation == 270))
                        {
                            if (pageOrientation == 90)
                            {
                                content.AddTemplate(importedPage, 0, -1f, 1f, 0, 0,
                                   reader.GetPageSizeWithRotation(currentPageIndex).Height);
                            }
                            if (pageOrientation == 270)
                            {
                                content.AddTemplate(importedPage, 0, 1f, -1f, 0,
                                   reader.GetPageSizeWithRotation(currentPageIndex).Width, 0);
                            }
                        }
                        else
                        {
                            content.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                throw new Exception("There has an unexpected exception" +
                      " occured during the pdf merging process.", exception);
            }
            finally
            {
                document.Close();
            }
            return output.GetBuffer();
        }

        private bool IsUrl(string filePath)
        {
            if (filePath.StartsWith("http") || filePath.StartsWith("ftp"))
                return true;
            return false;
        }

    }
    internal class PdfPageEvents : IPdfPageEvent
    {
        #region members
        private BaseFont _baseFont = null;
        private PdfContentByte _content;
        #endregion

        #region IPdfPageEvent Members
        public void OnOpenDocument(PdfWriter writer, Document document)
        {
            _baseFont = BaseFont.CreateFont(BaseFont.HELVETICA,
                             BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
            _content = writer.DirectContent;
        }

        public void OnStartPage(PdfWriter writer, Document document)
        { }

        public void OnEndPage(PdfWriter writer, Document document)
        {
            // Write header text
            string headerText = "";
            _content.BeginText();
            _content.SetFontAndSize(_baseFont, 8);
            _content.SetTextMatrix(GetCenterTextPosition(headerText,
                                   writer), writer.PageSize.Height - 10);
            _content.ShowText(headerText);
            _content.EndText();

            // Write footer text (page numbers)
            string text = "Page " + writer.PageNumber;
            _content.BeginText();
            _content.SetFontAndSize(_baseFont, 8);
            _content.SetTextMatrix(GetCenterTextPosition(text, writer), 10);
            _content.ShowText(text);
            _content.EndText();
        }

        public void OnCloseDocument(PdfWriter writer, Document document)
        { }

        public void OnParagraph(PdfWriter writer,
                    Document document, float paragraphPosition)
        { }

        public void OnParagraphEnd(PdfWriter writer,
                    Document document, float paragraphPosition)
        { }

        public void OnChapter(PdfWriter writer, Document document,
                              float paragraphPosition, Paragraph title)
        { }

        public void OnChapterEnd(PdfWriter writer,
                    Document document, float paragraphPosition)
        { }

        public void OnSection(PdfWriter writer, Document document,
                    float paragraphPosition, int depth, Paragraph title)
        { }

        public void OnSectionEnd(PdfWriter writer,
                    Document document, float paragraphPosition)
        { }

        public void OnGenericTag(PdfWriter writer, Document document,
                                 Rectangle rect, string text)
        { }
        #endregion

        private float GetCenterTextPosition(string text, PdfWriter writer)
        {
            return writer.PageSize.Width / 2 - _baseFont.GetWidthPoint(text, 8) / 2;
        }
    }
}
